//-----------------------------------------------------------------------------
//
// kcache.c
//
// Group:
//    Doug Kline,          dakline@ncsu.edu
//    Mauricio Del Carpio, mgdelcar@ncsu.edu
//    Josh Jodrey,         jdjodrey@ncsu.edu
//
// Defines the entry point for the proxy server.
//
// printk() outputs are on these levels:
//
// KERN_ERR
// KERN_INFO
// KERN_DEBUG
//-----------------------------------------------------------------------------

// TODO how many of these are needed?
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/syscalls.h>
#include <net/sock.h>
#include <asm/processor.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/kthread.h>

#include <linux/ioport.h>
#include "ksocket.h"
#include "config.h"
#include "NAT.h"

static struct Config g_config;

// Make the sysfs entrys.  Each entry needs to have read and write
// access.
//
// start - 1 starts the proxy server, 0 stops the proxy server
static int start = 0;
module_param(start, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP );

// TODO - remove the debug flag and just use KERN_DEBUG
//
// debug - 1 debug output,          0 no debug output
int debug = 0;
module_param(debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP );

// info - 1 info output, 0 no info output
int info = 1;
module_param(info, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP );

// File scoped
static struct task_struct *g_watcherTask_p = NULL;
static struct task_struct *g_serverTask_p  = NULL;

int g_serverStarted = 0;
int g_serverDied    = 0;

// Forward decl's
int kcache_init_module(void);
void kcache_exit_module(void);
static int runServer(void *arg);
int kcacheWatcher(void *arg);

// I don't yet know how to add these multiple .c files to the Makefile
// so for now just pull them into this file.
#include "config.c"
#include "ksocket.c"
#include "proxy.c"
#include "reqHandler.c"
#include "serverHandler.c"
#include "cache.c"
#include "NAT.c"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kline, Del Carpio, Jodrey");



//----------------------------------------------------------------------------
//
// kcache_init_module()
//
// This is the entry point for the kernel module.
//
//----------------------------------------------------------------------------
int kcache_init_module(void)
{

	int err = 0;

   // Start a thread to oversee this module.  This thread
   // will watch the start and debug flags.
   //
   // kernel_thread() invokes do_fork()
   //
   printk(KERN_INFO "\n\n\n\n#########################################################\n");
   printk(KERN_INFO "Entered kcache server\n");

   err = initProcFile();
   if (err)
   {
      printk(KERN_ERR "Failed to initialize the /proc/kcache directory\n");
      return(-1);
   }


   g_watcherTask_p = kthread_create(kcacheWatcher, NULL, "%s", "watcher");

   if (IS_ERR(g_watcherTask_p))
   {
      printk(KERN_ERR "ERROR: failed to start watcher task\n");
   }
   else
   {
      wake_up_process(g_watcherTask_p);
   } 
   
   return(0);
   
 } // end kcache_init_module()
 
 

//----------------------------------------------------------------------------
//
// kcache_exit_module()
//
// This is the exit point for the kernel module.
//
//----------------------------------------------------------------------------
void kcache_exit_module(void)
{
   // Stop the watcher thread and give it a chance to end
   if (g_watcherTask_p)
   {
      printk(KERN_DEBUG "Stopping kcache watcher thread\n");
      kthread_stop(g_watcherTask_p);
      msleep_interruptible(110);
   }
   
   // Clean up the NAT if installed
   if (natInstalled())
   {
      natExit();
   }

   // Stop the server thread and give it a chance to end
   if (g_serverStarted && !g_serverDied && g_serverTask_p)
   {
      printk(KERN_DEBUG "Stopping kcache server thread\n");
      kthread_stop(g_serverTask_p);
      g_serverTask_p = NULL;
      g_serverStarted = 0;
      msleep_interruptible(110);
   }
            
   closeProcFile();
 
   printk(KERN_INFO "Exiting kcache server\n");

} // end kcache_exit_module()




//----------------------------------------------------------------------------
//
// kcacheWatcher()
//
// When the kernel module is created, a new thread is spun off into this
// routine to handle the module.  This function watches for changes in the
// sys/module/kcache/start file to trigger the server.
//
//----------------------------------------------------------------------------
int kcacheWatcher(void *arg)
{
   int err;
   
   while( 1 )
   {
      // Sleep and then periodically check things out
      msleep_interruptible(100);
      
      if( kthread_should_stop())
      {
         break;
      }
      
      if (g_serverDied)
      {
         g_serverStarted = 0;
         g_serverDied    = 0;    // Reset on the watcher thread, set on the server thread
         g_serverTask_p  = NULL;
      }
      
       // Read the sysfs variable
      if(start != 0)
      {
         if (g_serverStarted == 0)
         {
            printk(KERN_DEBUG "Starting kcache server\n");
        
            // Start a separate thread to run the server
            
            g_serverTask_p = kthread_create(runServer, NULL, "%s", "server");

            if ((g_serverTask_p == NULL) || (IS_ERR(g_serverTask_p)))
            {
               printk(KERN_ERR "ERROR: failed to start kcache server task\n");
            }
            else
            {
               g_serverStarted = 1;
               wake_up_process(g_serverTask_p);
            }
         }

         if ( natInstalled() == 0 )
         {
            // Hook into the IP forwarding
            err = natInit(); 
            if (err)
            {
               printk(KERN_ERR "ERROR: failed to initialize the NAT module\n");
               return(-1);
            }
         }
 
      }
      else // start == 0
      {
         if (g_serverStarted == 1)
         {
            printk(KERN_DEBUG "Stopping kcache server\n");
            kthread_stop(g_serverTask_p);
            g_serverTask_p = NULL;
            g_serverStarted = 0;
         }

         if ( natInstalled() )
         {
            // Remove the hook into IP forwarding
            err = natExit();
            if (err)
            {
               printk(KERN_ERR "ERROR: failed to remove the NAT module\n");
               return(-1);
            }
         }

      }
   
   }
   
   printk(KERN_DEBUG "Exiting kcache watcher thread\n");
   return 0;

} // end kcacheWatcher()

module_init(kcache_init_module);
module_exit(kcache_exit_module);


